<!DOCTYPE html>
<html lang="zh"><head><meta charset="UTF-8"><title>五子棋</title><style>
  html,body{ margin: 0; padding: 0; height: 100%; }
  canvas{
    width: 500px;
    height: 600px;
    margin: 20px;
  }
</style></head><body>
  <canvas width="500" height="600"></canvas>
<script>
  // 基础配置
  var config=(function(width,height,row,col,...colorList){
    var col_one=width/(col+1);
    var row_one=height/(row+1);
    /**
     * 棋盘
     * 0=可下棋子
     * 1=选手A
     * 2=选手B
     * ...
     */
    var board=new Array(row).fill(0);
    board=board.map(o=>new Array(col).fill(o));
    return {
      width,
      height,
      clientWidth:width-col_one,
      clientHeight:height-row_one,
      col,
      row,
      col_one,
      row_one,
      board,
      colorList,
      player:0,
    };
  }(500,500,20,20,"#f00","#00f"));
  // 画板元素
  var canvas=document.querySelector("canvas");
  // 绘画句柄
  var _2d=canvas.getContext("2d");
  /**
   * 绘制背景网格
   */
  var draw_back_grid=function(){
    // 绘制背景
    _2d.fillStyle="#ccc";
    _2d.fillRect(0,0,canvas.width,canvas.height);
    _2d.strokeStyle="#000";
    // 绘制横向网格
    for(var i=0; i<=config.row+1; i++){
      var y=i*config.row_one;
      _2d.beginPath();
      _2d.moveTo(0,y);
      _2d.lineTo(config.width,y);
      _2d.stroke();
      _2d.closePath();
    }
    // 绘制纵向网格
    for(var i=0; i<=config.col+1; i++){
      var x=i*config.col_one;
      _2d.beginPath();
      _2d.moveTo(x,0);
      _2d.lineTo(x,config.height);
      _2d.stroke();
      _2d.closePath();
    }
    // 绘制棋子
    var margin={width:config.col_one, height:config.row_one};
    var size=(margin.width>margin.height?margin.height:margin.width)*0.7;
    _2d.strokeStyle="#666";
    for(var i=0; i<config.row; i++){
      for(var j=0; j<config.col; j++){
        var index=config.board[i][j];
        if(index>0 && index<=config.colorList.length){
          _2d.fillStyle=config.colorList[index-1];
          var x=j*config.col_one+margin.width;
          var y=i*config.row_one+margin.height;
          _2d.beginPath();
          _2d.arc(x,y,size/2,0,2*Math.PI);
          _2d.fill();
          _2d.closePath();
          _2d.beginPath();
          _2d.arc(x,y,size/2,0,2*Math.PI);
          _2d.stroke();
          _2d.closePath();
        }
      }
    }
    // 绘制棋手信息
    var index=config.player;
    var y=config.height+20;
    _2d.fillStyle=config.colorList[index];
    _2d.fillRect(8,y-14,16,16);
    _2d.fillStyle="#000";
    _2d.font="16px 微软雅黑";
    _2d.fillText(`有请${index+1}号选手下棋!`,30,y);
  };
  /**
   * 鼠标单击棋盘
   */
  var canvas_mousedown=null;
  /**
   * 游戏结束
   */
  var game_over=function(index){
    canvas.removeEventListener("mousedown",canvas_mousedown);
    _2d.fillStyle="#ccc";
    _2d.fillRect(0,0,canvas.width,canvas.height);
    var y=config.height/2;
    _2d.fillStyle=config.colorList[index];
    _2d.fillRect(8,y-14,16,16);
    _2d.fillStyle="#000";
    _2d.font="16px 微软雅黑";
    _2d.fillText(`玩家${index+1}赢得游戏，掌声鼓励!`,30,y);
  };
  /**
   * 游戏步骤 判断是否结束
   */
  var game_process=function(X,Y,Value){
    // 判断左向同一玩家棋子
    var leftCount=0;
    if(X>0){
      for(var x=X-1; x>=X-4; x--){
        if(config.board[Y]&&config.board[Y][x]==Value)leftCount++;
        else break;
      }
    }
    // 判断右向同一玩家棋子
    var rightCount=0;
    if(X<config.col-1){
      for(var x=X+1; x<=X+4; x++){
        if(config.board[Y]&&config.board[Y][x]==Value)rightCount++;
        else break;
      }
    }
    // 左右两边加棋子本身大于5
    if(leftCount+rightCount+1 >= 5){
      return true;
    }
    // 判断上方同一玩家棋子
    var upCount=0;
    if(Y>0){
      for(var y=Y-1; y>=Y-4; y--){
        if(config.board[y]&&config.board[y][X]==Value)upCount++;
        else break;
      }
    }
    // 判断下方同一玩家棋子
    var bottomCount=0;
    if(Y<config.row-1){
      for(var y=Y+1; y<=Y+4; y++){
        if(config.board[y]&&config.board[y][X]==Value)bottomCount++;
        else break;
      }
    }
    // 上下两边加棋子本身大于5
    if(upCount+bottomCount+1 >= 5){
      return true;
    }
    // 判断左上方同一玩家棋子
    var leftupCount=0;
    for(var i=-1; i>=-4; i--){
      if(config.board[Y+i]&&config.board[Y+i][X+i]==Value)leftupCount++;
      else break;
    }
    // 判断右下方同一玩家棋子
    var rightbottomCount=0;
    for(var i=1; i<=4; i++){
      if(config.board[Y+i]&&config.board[Y+i][X+i]==Value)rightbottomCount++;
      else break;
    }
    // 左上到右下加棋子本身大于5
    if(leftupCount+rightbottomCount+1 >= 5){
      return true;
    }
    // 判断左下方同一玩家棋子
    var leftbottomCount=0;
    for(var i=-1; i>=-4; i--){
      if(config.board[Y-i]&&config.board[Y-i][X+i]==Value)leftbottomCount++;
      else break;
    }
    // 判断右上方同一玩家棋子
    var rightupCount=0;
    for(var i=1; i<=4; i++){
      if(config.board[Y-i]&&config.board[Y-i][X+i]==Value)rightupCount++;
      else break;
    }
    // 左下到右上加棋子本身大于5
    if(leftbottomCount+rightupCount+1 >= 5){
      return true;
    }
    return false;
  };
  canvas_mousedown=function(e){
    var pos={X:e.offsetX-config.col_one/2, Y:e.offsetY-config.row_one/2};
    var index={
      X:(pos.X<0||pos.X>config.clientWidth)?-1:parseInt(pos.X/config.col_one),
      Y:(pos.Y<0||pos.Y>config.clientHeight)?-1:parseInt(pos.Y/config.row_one),
    };
    if(index.X>-1 && index.Y>-1 && config.board[index.Y][index.X]==0){
      config.board[index.Y][index.X]=config.player+1;
      if(game_process(index.X,index.Y,config.player+1)){
        game_over(config.player);
      }else{
        config.player++;
        if(config.player>=config.colorList.length){
          config.player=0;
        }
        draw_back_grid();
      }
    }
  };
  canvas.addEventListener("mousedown",canvas_mousedown);
  draw_back_grid();
</script></body></html>